/**
 * EasyTextArea.java
 * @author Daniel Liu
 * Create: 2007-1-9 ����07:10:39
 * NOTE: 
 */
package org.lex.swing.widgets;

import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;

import org.lex.swing.ActionBuilder;
import org.lex.swing.DocumentAdapter;
import org.lex.swing.Swings;
import org.lex.swing.fontchooser.JFontChooser;
import org.lex.utils.Changeable;


public class EnhancedTextArea extends JTextArea implements Changeable {
	public static void main(String[] args) {
		Swings.show(new JScrollPane(new EnhancedTextArea()));
	}

	private AutoScrollAction autoScrollAction = null;
	private LineWrapAction lineWrapAction = null;
	private WordWrapAction wordWrapAction = null;
	private FontAction fontAction = null;
	private TabSizeAction tabSizeAction = null;
	private ClearTextAction clearTextAction = null;
	// for changeable support
	private boolean hasDocumentChanged = false;
	private DocumentListener changeListener = null;
	// for anti-alias support
	private Object textAntiAliasHint = RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT;

	public EnhancedTextArea() {
		super();
		this.setComponentPopupMenu(new DefaultPopupMenu());
		this.changeListener = new DocumentAdapter() {
			public void documentChanged(DocumentEvent e) {
				setDocumentChanged(true);
				if (getAutoScrollAction().isSelected())
					moveCaretToEnd();
			}
		};
		this.getDocument().addDocumentListener(changeListener);
	}

	public void setLineWrap(boolean wrap) {
		this.getLineWrapAction().setSelected(wrap);
		if (true == this.getAutoScrollAction().isSelected())
			moveCaretToEnd();
		this.getWordWrapAction().setEnabled(wrap);
	}

	public void setWrapStyleWord(boolean wrap) {
		this.getWordWrapAction().setSelected(wrap);
		if (true == this.getAutoScrollAction().isSelected())
			moveCaretToEnd();
	}

	public void setDocument(Document doc) {
		Document old = this.getDocument();
		if (null != old)
			old.removeDocumentListener(changeListener);
		super.setDocument(doc);
		if (null != doc)
			doc.addDocumentListener(changeListener);
	}

	public boolean isAutoScrollText() {
		return this.getAutoScrollAction().isSelected();
	}

	public void setAutoScrollText(boolean autoScrollText) {
		this.getAutoScrollAction().setSelected(autoScrollText);
	}

	public void moveCaretToBegin() {
		this.setCaretPosition(0);
	}

	public void moveCaretToEnd() {
		this.setCaretPosition(getDocument().getLength());
	}

	public void clear() {
		this.setText("");
		this.setDocumentChanged(false);
	}

	// Changable support
	public boolean hasContentChanged() {
		return hasDocumentChanged;
	}

	protected void setDocumentChanged(boolean value) {
		this.hasDocumentChanged = value;
	}

	// text anti-alias support
	public void setTextAntialiasEnabled(boolean enabled) {
		if (true == enabled)
			this.textAntiAliasHint = RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
		else
			this.textAntiAliasHint = RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;
	}

	public boolean isTextAntialiasEnabled() {
		return this.textAntiAliasHint == RenderingHints.VALUE_TEXT_ANTIALIAS_ON;
	}

	protected void paintComponent(Graphics g) {
		// FIXME altialias �������ƺ�û��������...
		Graphics2D g2 = (Graphics2D) g;
		g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, textAntiAliasHint);
		super.paintComponent(g2);
	}

	// ==========================================
	public AutoScrollAction getAutoScrollAction() {
		if (null == autoScrollAction)
			autoScrollAction = new AutoScrollAction();
		return autoScrollAction;
	}

	public LineWrapAction getLineWrapAction() {
		if (null == lineWrapAction)
			lineWrapAction = new LineWrapAction();
		return lineWrapAction;
	}

	public WordWrapAction getWordWrapAction() {
		if (null == wordWrapAction)
			wordWrapAction = new WordWrapAction();
		return wordWrapAction;
	}

	public FontAction getFontAction() {
		if (null == fontAction)
			fontAction = new FontAction();
		return fontAction;
	}

	public TabSizeAction getTabSizeAction() {
		if (null == this.tabSizeAction)
			this.tabSizeAction = new TabSizeAction();
		return this.tabSizeAction;
	}

	public ClearTextAction getClearTextAction() {
		if (null == clearTextAction)
			clearTextAction = new ClearTextAction();
		return clearTextAction;
	}

	// ========================================
	private void delegateSetLineWrapOfSuper(boolean wrap) {
		super.setLineWrap(wrap);
	}

	private void delegatesetWrapStyleWordOfSuper(boolean wrap) {
		super.setWrapStyleWord(wrap);
	}

	class DefaultPopupMenu extends JPopupMenu {
		private JCheckBoxMenuItem autoScroll = null;
		private JCheckBoxMenuItem lineWrap = null;
		private JCheckBoxMenuItem wordWrap = null;
		private JMenuItem font = null;
		private JMenuItem tabSize = null;
		private JMenuItem clear = null;

		DefaultPopupMenu() {
			super();
			// ========================================
			autoScroll = new JCheckBoxMenuItem(getAutoScrollAction());
			autoScroll.setName(getClass().getCanonicalName() + ".autoScrollMenu");
			lineWrap = new JCheckBoxMenuItem(getLineWrapAction());
			lineWrap.setName(getClass().getCanonicalName() + ".lineWrapMenu");
			wordWrap = new JCheckBoxMenuItem(getWordWrapAction());
			wordWrap.setName(getClass().getCanonicalName() + ".wordWrapMenu");
			font = new JMenuItem(getFontAction());
			tabSize = new JMenuItem(getTabSizeAction());
			clear = new JMenuItem(getClearTextAction());
			// =========================================
			this.add(autoScroll);
			this.add(lineWrap);
			this.add(wordWrap);
			this.add(font);
			this.add(tabSize);
			this.addSeparator();
			this.add(clear);
		}
	}

	class AutoScrollAction extends ActionBuilder {
		AutoScrollAction() {
			super();
			this.setName("Auto Scroll");
			this.setSelected(false);
			this.setMnemonic(KeyEvent.VK_S);
		}

		public void actionPerformed(ActionEvent e) {
			if (true == this.isSelected())
				moveCaretToEnd();
		}
	}

	class LineWrapAction extends ActionBuilder {
		LineWrapAction() {
			super();
			this.setName("Wrap Line");
			this.setSelected(false);
			this.setMnemonic(KeyEvent.VK_L);
		}

		public void actionPerformed(ActionEvent e) {
			boolean selected = this.isSelected();
			delegateSetLineWrapOfSuper(selected);
			wordWrapAction.setEnabled(selected);
		}
	}

	class WordWrapAction extends ActionBuilder {
		WordWrapAction() {
			super();
			this.setName("Wrap Word");
			this.setSelected(false);
			this.setEnabled(false);
			this.setMnemonic(KeyEvent.VK_W);
		}

		public void actionPerformed(ActionEvent e) {
			delegatesetWrapStyleWordOfSuper(this.isSelected());
			EnhancedTextArea.this.repaint();
		}
	}

	class TabSizeAction extends ActionBuilder {
		TabSizeAction() {
			super();
			this.setName("Tab Size...");
			this.setMnemonic(KeyEvent.VK_T);
		}

		public void actionPerformed(ActionEvent e) {
			int old = getTabSize();
			Object value = JOptionPane.showInputDialog(root(), "Set the tab size:", "Tab Size", JOptionPane.QUESTION_MESSAGE, null, null, old);
			if (null == value)
				return;
			try {
				int size = Integer.parseInt(String.valueOf(value));
				setTabSize(size);
			} catch (Exception ex) {
				setTabSize(old);
			}
		}

		private Window root() {
			return SwingUtilities.getWindowAncestor(EnhancedTextArea.this);
		}
	}

	class FontAction extends ActionBuilder {
		FontAction() {
			super();
			this.setName("Font...");
			this.setMnemonic(KeyEvent.VK_F);
		}

		public void actionPerformed(ActionEvent e) {
			Font font = JFontChooser.showDialog(EnhancedTextArea.this, "Font", getFont());
			if (null != font) {
				setFont(font);
			}
		}
	}

	class ClearTextAction extends ActionBuilder {
		ClearTextAction() {
			super();
			this.setName("Clear Text");
			this.setMnemonic(KeyEvent.VK_C);
		}

		public void actionPerformed(ActionEvent e) {
			setText("");
		}
	}
}
